// 网格热力，作为混入提供给热力图使用--不同数量级的颜色不同，有文字提示
import GeoJSON from 'ol/format/GeoJSON'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import { Fill, Stroke, Style } from 'ol/style'
import Text from 'ol/style/Text'
import axios from 'axios'
import Polygon from 'ol/geom/Polygon'
import { transform, getTransform } from 'ol/proj' // 坐标转换
import Feature from 'ol/Feature'
import Point from 'ol/geom/Point' // 创建txt信息点用

export default {
  data () {
    return {
      // oMap: null, // 地图对象在heatmaplayer中
      // sSRS: 'EPSG:4326', // 空间参照系（Spatial Reference System) ?sRCS(Reference Coordinate System)
      // gridLayer: null // 在heatmaplayer中，（主）
      // nMax: 2000, // {Number}热力值最大取值范围(主)
      sAttach: null, // 附加 如网格热力图 是其使用的网格名称
      aGridStyleLs: [], // 热力网格样式列表，根据aGradient（主）计算生成
      sGridColor: '#666666', // 网格热力图的网格边框颜色
      nGridWidth: 1, // 网格热力图的网格边框线宽
      nAlpha: 0.6 // 网格染色透明度
      // txtPoint: null // 显示文字信息的坐标点！
    }
  },
  // watch: {
  //   // watch
  // },
  // mounted () {
  //   // mounted
  // },
  methods: {
    /**
     * @description 创建gridheat层，并添加到地图中
     * @param null
     * @return {Object} 新创建的网格热力图层
     */
    createHeatGrid () {
      // 需要一个vector的layer来放置网格数据
      // return new HeatmapLayer({ //Heatmap ol.layer.Vector
      return new VectorLayer({
        source: new VectorSource(), // {
        // url: '/mapres/grid/南宁市.json',
        // format: new GeoJSON() // ol.format.GeoJSON
        // }),
        wrapX: false,
        style: new Style({ // 边线样式
          stroke: new Stroke({
            color: '#333333',
            width: 2
          }),
          fill: new Fill({ // 填充色
            color: 'rgba(255,0,0,0)'
          })
        })
      })
    },

    /**
     * @description 销毁heatgrid层；（父中存在-直接调用）
     * @param null
     * @return null
     */
    // destroyHeatLayer () 清空数据删除heatmap层，在heatgrid
    /**
     * @description 清空热力数据；（父中存在-直接调用）
     * @param  null
     * @return null
     */
    // clearHeatData () 删除热力数据（一般网格不变时不需要删除，否则会重新调入网格）

    /**
     * @description 清空heatgrid数据；将颜色和数据清除 (需要验证)
     * @param  null
     * @return null
     */
    clearHeatGridData () {
      // 如果有文字信息，先删除
      const txtFeature = this.heatmapLayer.getSource().getFeatureById('hotxt')
      if (txtFeature) {
        this.heatmapLayer.getSource().removeFeature(txtFeature)
      }
      // 将样式设置为sytle[0](空网格)
      console.log(this.heatmapLayer)
      const oGridList = this.heatmapLayer.getSource().getFeatures()
      let oGrid
      for (let i = 0; i < oGridList.length; i++) {
        oGrid = oGridList[i] // 获取每个网格进行处理
        oGrid.setStyle(this.aGridStyleLs[0])
      }
    },

    /**
     * @description 创建网格样式文件
     * @param  sfill {String} 填充颜色; sStrokeColor {String}边线颜色；nStrokeWidth {Number}边框宽度（像素）
     * @return 创建的新样式
     */
    createHeatgridStyle (sfill, sStrokeColor = '#333333', nStrokeWidth = 2) {
      return new Style({ // 边线样式
        stroke: new Stroke({
          color: sStrokeColor, // '#333333',
          width: nStrokeWidth // 2
        }),
        fill: new Fill({ // 填充色
          color: sfill // 'rgba(255,0,0,0)'
        })
      })
    },

    /**
     * @description 初始化网格基础颜色（16种）0-1-2-3-4 区间，每区间有4个颜色
     * @param  aColorArray {Array} 颜色数组（参数属性）; nStep {Number} 区间内个数，默认是4
     * @return null
     */
    initGridStyleList (aColorArray, nStep = 4) {
      const nColorNum = aColorArray.length
      // 清空网格热力样式列表
      this.aGridStyleLs.splice(0, nColorNum)
      // 颜色字符串
      let sRGBA
      sRGBA = 'rgba(255, 0, 0, 0)'
      // 将新生成的样式添加到样式表 this.aGridStyleLs.push() ；索引0时空的网格样式，默认的
      this.aGridStyleLs.push(this.createHeatgridStyle(sRGBA, this.sGridColor, this.nGridWidth))

      // 计算各个热力颜色信息
      let startColor // 开始颜色
      let endColor // 结束颜色
      let sHex // 十六进制标志位
      let sHexColor // 十六进制颜色位
      let nStartR // 开始颜色的红色
      let nStartG // 开始颜色的绿色n
      let nStartB // 开始颜色的蓝色
      let sEndR // 结束颜色的红色
      let sEndG // 结束颜色的绿色
      let sEndB // 结束颜色的蓝色

      for (let i = 0; i < nColorNum - 1; i++) {
        startColor = aColorArray[i] // 获取开始颜色
        endColor = aColorArray[i + 1] // 获取结束颜色
        // 开始色处理
        sHex = startColor.substr(0, 1)
        sHexColor = startColor.substr(1)
        if (sHex === '#') {
          let sBit
          if (sHexColor.length === 3) {
            sBit = sHexColor.substr(0, 1)
            nStartR = parseInt(sBit + sBit, 16)
            sBit = sHexColor.substr(1, 1)
            nStartG = parseInt(sBit + sBit, 16)
            sBit = sHexColor.substr(2, 1)
            nStartB = parseInt(sBit + sBit, 16)
          } else {
            sBit = sHexColor.substr(0, 2)
            nStartR = parseInt(sBit, 16)
            sBit = sHexColor.substr(2, 2)
            nStartG = parseInt(sBit, 16)
            sBit = sHexColor.substr(4, 2)
            nStartB = parseInt(sBit, 16)
          }
        }
        // 结束色处理
        sHex = endColor.substr(0, 1)
        sHexColor = endColor.substr(1)
        if (sHex === '#') {
          let sBit
          if (sHexColor.length === 3) {
            sBit = sHexColor.substr(0, 1)
            sEndR = parseInt(sBit + sBit, 16)
            sBit = sHexColor.substr(1, 1)
            sEndG = parseInt(sBit + sBit, 16)
            sBit = sHexColor.substr(2, 1)
            sEndB = parseInt(sBit + sBit, 16)
          } else {
            sBit = sHexColor.substr(0, 2)
            sEndR = parseInt(sBit, 16)
            sBit = sHexColor.substr(2, 2)
            sEndG = parseInt(sBit, 16)
            sBit = sHexColor.substr(4, 2)
            sEndB = parseInt(sBit, 16)
          }
        }
        // 计算颜色信息
        const nUnit = 1 / nStep
        let nCost
        let nSub
        let nTempR
        let nTempG
        let nTempB
        // 颜色字符串（首先是开始的颜色）
        // let sRGBA
        sRGBA = 'rgba(' + nStartR + ',' + nStartG + ',' + nStartB + ',' + this.nAlpha + ')'
        // 颜色阈，开始的样式添加
        this.aGridStyleLs.push(this.createHeatgridStyle(sRGBA, this.sGridColor, this.nGridWidth))

        // 先将开始色添加到颜色数组，作为开始色；因此下面循环从1开始；注意：最后一个元素的结束色要添加到颜色数组
        for (let j = 1; j < nStep; j++) {
          nCost = nUnit * j
          nSub = 1 - nCost
          nTempR = Math.round(nStartR * nSub + sEndR * nCost)
          if (nTempR > 255) {
            nTempR = 255
          }
          nTempG = Math.round(nStartG * nSub + sEndG * nCost)
          if (nTempG > 255) {
            nTempG = 255
          }
          nTempB = Math.round(nStartB * nSub + sEndB * nCost)
          if (nTempB > 255) {
            nTempB = 255
          }
          // 将数字转换成字符串
          sRGBA = 'rgba(' + nTempR + ',' + nTempG + ',' + nTempB + ',' + this.nAlpha + ')'
          // 颜色阈内样式的添加
          this.aGridStyleLs.push(this.createHeatgridStyle(sRGBA, this.sGridColor, this.nGridWidth))
        }
      }
      sRGBA = 'rgba(' + sEndR + ',' + sEndG + ',' + sEndB + ',' + this.nAlpha + ')'
      // 最有一个样式的结束，后续没有阈值。因此要使用最后一个阈的结束色作为这个颜色阈的样式结束 f00
      this.aGridStyleLs.push(this.createHeatgridStyle(sRGBA, this.sGridColor, this.nGridWidth))
    },

    /** 显示隐藏 + 透明度 + 样式调整 */

    /**
     * @description 网格样式设置；（样式配置很多，后续完成优化）
     * @param oStyle {Object}
     * @return null
     */
    setHeatgridStyle (oStyle) {
      const sStrokeColor = oStyle.stroke // 网格边框颜色
      const nStrokeWidth = oStyle.width // 网格边框宽度（像素）
      const nOpacity = oStyle.opacity // 网格染色透明度
      const nMinZoom = oStyle.minZ // 最小显示级别
      const nMaxZoom = oStyle.maxZ // 最大显示级别
      const aGradient = oStyle.aGradient // 热力图颜色阈值
      if (sStrokeColor) {
        this.sGridColor = sStrokeColor
      }
      if (nStrokeWidth) {
        this.nGridWidth = nStrokeWidth
      }
      if (nOpacity) {
        this.nAlpha = nOpacity
      }
      if (nMinZoom) {
        this.heatmapLayer.setMinZoom(nMinZoom)
      }
      if (nMaxZoom) {
        this.heatmapLayer.setMaxZoom(nMaxZoom)
      }
      if (aGradient) {
        this.aGradient = aGradient
      }
      // 样式改变后，要重新生成 网格样式(重新生成样式耗费资源，不建议修改)
      this.initGridStyleList(this.aGradient)
    },

    /**
     * @description 根据热力值计算样式索引
     * @param  nHotVal {Number} 热力值
     * @return 返回样式索引值
     */
    getStyleIndex (nHotVal) {
      if (!nHotVal || this.aGridStyleLs.length !== 18) return 0 // null, undef, 0 || 颜色属性默认18个
      // aGridStyleLs 0: 无色多边形样式;后续16区间
      const nRatio = nHotVal / this.nMax
      if (nRatio > 1) return this.aGridStyleLs.length - 1 // 返回最大样式
      if (nRatio >= 0 < 0.0625) { //
        return 1
      } else if (nRatio >= 0.0625 < 0.125) {
        return 2
      } else if (nRatio >= 0.125 < 0.1875) {
        return 3
      } else if (nRatio >= 0.1875 < 0.25) {
        return 4
      } else if (nRatio >= 0.25 < 0.3125) {
        return 5
      } else if (nRatio >= 0.3125 < 0.375) {
        return 6
      } else if (nRatio >= 0.375 < 0.4375) {
        return 7
      } else if (nRatio >= 0.4375 < 0.5) {
        return 8
      } else if (nRatio >= 0.5 < 0.5625) {
        return 9
      } else if (nRatio >= 0.5625 < 0.625) {
        return 10
      } else if (nRatio >= 0.625 < 0.6875) {
        return 11
      } else if (nRatio >= 0.6875 < 0.75) {
        return 12
      } else if (nRatio >= 0.75 < 0.8125) {
        return 13
      } else if (nRatio >= 0.8125 < 0.875) {
        return 14
      } else if (nRatio >= 0.875 < 0.9375) {
        return 15
      } else if (nRatio >= 0.9375 < 1) {
        return 16
      } else { // 大于1时
        return 17
      }
    },

    /**
     * @description 获取网格数据（获取网格的json数据）
     * @param  sGridName {String} 使用的网格名称
     * @return 网格json数据
     */
    getGridData (sGridName) {
      const sJsonUrl = '/mapres/grid/' + sGridName + '.json'
      return new Promise(function (resolve) {
        axios.get(sJsonUrl).then(
          (res) => { resolve(res) }, // 成功
          (err) => {
            console.log(err) // 失败
            resolve(null)
          }
        )
      })
    },

    /** 此方法作废
     * @description 加载热力图网格--基础网格(有延迟问题-异步造成) 由下面的loadGrid2Map替代
     * @param  sGridName {String} 使用的网格名称
     * @return null
     */
    async loadHeatGridBase (sGridName) {
      // console.log(sGridName)
      const oGridJson = await this.getGridData(sGridName)
      if (oGridJson) {
        // 获取视图投影(当前环境使用的)
        const sViewProj = this.oMap.getView().getProjection()
        // console.log(oGridJson.data)
        let oFeature
        let gridCoord // 多边形的坐标
        let gridFeature = null // 要创建的Faeture
        const gridFeatureArr = [] // 热点信息数组（保存生成的每个热点网格gridFeature）
        let nStyleID = 0 // 网格样式索引，在aGridStyleLs中
        for (let i = 0; i < oGridJson.data.features.length; i++) {
          oFeature = oGridJson.data.features[i]
          if (oFeature.geometry.type === 'Polygon') {
            gridCoord = oFeature.geometry.coordinates
            const oGeometry = new Polygon(gridCoord)
            if (sViewProj.getCode() !== this.sSRS) {
              oGeometry.applyTransform(getTransform(this.sSRS, sViewProj.getCode())) // ol.proj.getTransform
              // console.log(oGeometry.getCoordinates())
            }
            // 创建多边形 Feature
            gridFeature = new Feature({ // ol.Feature
              geometry: oGeometry, // ol.geom.lineSting
              type: 'hot' // add for select
            })
            gridFeature.setId(oFeature.properties.id)
            gridFeature.set('iVal', oFeature.properties.hotVal)
            // 样式的设置
            nStyleID = 0 // this.getStyleIndex(oFeature.properties.hotVal) // 根据热力值获取样式索引
            gridFeature.setStyle(this.aGridStyleLs[nStyleID])
            gridFeatureArr.push(gridFeature)
          }
        }
        this.heatmapLayer.getSource().addFeatures(gridFeatureArr)
      } else {
        // console.log('没有找到网格数据，退出')
      }
    },

    /**
     * @description 加载热力图网格到地图--延迟等待
     * @param  sGridName {String} 使用的网格名称
     * @return null
     */
    loadGrid2Map (oGridJson) {
      return new Promise((resolve, reject) => {
        if (oGridJson) {
          // 获取视图投影(当前环境使用的)
          const sViewProj = this.oMap.getView().getProjection()
          // console.log(oGridJson.data)
          let oFeature
          let gridCoord // 多边形的坐标
          let gridFeature = null // 要创建的Faeture
          const gridFeatureArr = [] // 热点信息数组（保存生成的每个热点网格gridFeature）
          let nStyleID = 0 // 网格样式索引，在aGridStyleLs中
          for (let i = 0; i < oGridJson.data.features.length; i++) {
            oFeature = oGridJson.data.features[i]
            if (oFeature.geometry.type === 'Polygon') {
              gridCoord = oFeature.geometry.coordinates
              const oGeometry = new Polygon(gridCoord)
              if (sViewProj.getCode() !== this.sSRS) {
                oGeometry.applyTransform(getTransform(this.sSRS, sViewProj.getCode())) // ol.proj.getTransform
                // console.log(oGeometry.getCoordinates())
              }
              // 创建多边形 Feature
              gridFeature = new Feature({ // ol.Feature
                geometry: oGeometry, // ol.geom.lineSting
                type: 'hot' // add for select
              })
              gridFeature.setId(oFeature.properties.id)
              gridFeature.set('iVal', oFeature.properties.hotVal)
              // 样式的设置
              nStyleID = 0 // this.getStyleIndex(oFeature.properties.hotVal) // 根据热力值获取样式索引
              gridFeature.setStyle(this.aGridStyleLs[nStyleID])
              gridFeatureArr.push(gridFeature)
            }
          }
          this.heatmapLayer.getSource().addFeatures(gridFeatureArr)
          resolve(true)
        } else {
          // console.log('没有找到网格数据，退出')
          reject(null)
        }
      })
    },

    /**
     * @description 读入heatgrid网格热力数据，并显示 grid 热力值
     * @param  oResData {Object}热力图数据对象, nIndex数据索引值（0-23）代表24小时（待定）
     * @return null
     */
    readHeatgrid (oSource) {
      let hotNode
      let oGridFeature
      const hotSource = this.heatmapLayer.getSource()
      let nStyleID = 0 // 样式
      // 读取解析每个Feature信息（数据必须包含所有热力框）
      for (let i = 0; i < oSource.features.length; i++) {
        // 获取一个热力值
        hotNode = oSource.features[i]
        console.log(i, hotNode)
        oGridFeature = hotSource.getFeatureById(hotNode.properties.id)
        // 设置新的热量值
        if (oGridFeature) {
          oGridFeature.set('iVal', hotNode.properties.hotVal)
          nStyleID = this.getStyleIndex(hotNode.properties.hotVal)
          oGridFeature.setStyle(this.aGridStyleLs[nStyleID])
        }
      }
      // -- demo --
      // let features = hotSource.getFeatures()
      // console.log(features)
      // for (let i = 0; i < features.length; i++) {
      //   features[i].setStyle(this.aGridStyleLs[16])
      // }
    },

    /**
     * @description 显示网格热力数据数据 grid
     * @param  oResData {Object}热力图数据对象, nIndex数据索引值（0-23）代表24小时（待定）
     * @return null
     */
    async showHeatGrid (oResData, nIndex = 0) {
      if (!oResData || JSON.stringify(oResData) === '{}') {
        return // 没有数据作处理
      }
      // let sourceProj = this.oMap.getView().getProjection()
      const nDataTimeNum = oResData.length
      console.log(oResData)
      if (nDataTimeNum === 0) return // 没有数据

      // 设置热力数据上限值
      if (oResData[0].nMax) {
        // console.log(oResData[0].nMax)
        this.nMax = oResData[0].nMax
      }
      // 如果热力图有样式数据需要设置

      if (oResData[0].style) {
        this.setHeatgridStyle(oResData[0].style)
        console.log('样式导入！')
        // 样式改变可能需要重绘网格
      }

      // 检查环境（颜色样式）是否就绪[根据颜色数组生成热力样式style]
      if (this.aGridStyleLs.length === 0) {
        this.initGridStyleList(this.aGradient)
        console.log(this.aGridStyleLs)
      }
      // 查看网格是否存在或与之前不一致(网格的绘制)
      if (this.sAttach === null || this.sAttach !== oResData[0].grid) {
        // 删除原先的网格数据
        this.clearHeatData() // 在heatmapLayer.vue中
        // 需要调入网格数据
        this.sAttach = oResData[0].grid
        // this.loadHeatGridBase(this.sAttach) 异步延迟，会造成无数据问题，由下面的语句代替
        await this.getGridData(this.sAttach).then((oGridData) => {
          // Promise异步依赖，参考https://www.jb51.net/article/118649.htm
          this.loadGrid2Map(oGridData)
        })
        console.log(this.sAttach)
      }

      // 如果只有一条数据，并且时间索引为0，直接生成热力图数据
      if (nIndex === 0 && nDataTimeNum === 1) {
        const oData = oResData[nIndex] // 获取一类数据
        const oGeoJson = oData.geojsonfeatures // 一个数据类型的geojson数据信息
        this.readHeatgrid(oGeoJson)
      } else {
        for (let i = 0; i < nDataTimeNum; i++) {
          const oData = oResData[i]
          if (oData.dataTime === nIndex) {
            const oData = oResData[nIndex] // 在数据中查找时间匹配的数据
            const oGeoJson = oData.geojsonfeatures // 一个数据类型的geojson数据信息
            this.readHeatgrid(oGeoJson)
            break
          }
        }
      }
      // 检查
      // console.log(this.heatmapLayer.getSource().getFeatures())
    },

    getHeatgridBBox (hotFeature) {
      const sType = hotFeature.getGeometry().getType()
      if (sType === 'Polygon') {
        if (hotFeature.getGeometry().getCoordinates().length === 0) return null // 多边形没有坐标数据！
        const aPolyCoord = hotFeature.getGeometry().getCoordinates()[0] // 多边形坐标[[ [x,y], [x1,y1],...[xn,yn] ]]
        const aCoordXList = []
        const aCoordYList = []
        let minX = null
        let minY = null
        let maxX = null
        let maxY = null
        // console.log(aPolyCoord)
        for (let i = 0; i < aPolyCoord.length; i++) {
          aCoordXList.push(aPolyCoord[i][0])
          aCoordYList.push(aPolyCoord[i][1])
        }
        // console.log(aCoordXList,aCoordYList)
        maxX = Math.max.apply(null, aCoordXList)
        minX = Math.min.apply(null, aCoordXList)
        maxY = Math.max.apply(null, aCoordYList)
        minY = Math.min.apply(null, aCoordYList)
        // 返回数据
        // console.log(aCoordXList, aCoordYList)
        // console.log(minX, minY, maxX, maxY)
        return [minX, minY, maxX, maxY]
      }
      return null // 不是多边形，不处理！
    },

    /**
     * @description 点击热力网格，显示热力值
     * @param  oResData {Object}热力图数据对象, nIndex数据索引值（0-23）代表24小时（待定）
     * @return null
     */
    clickheatgrid (hotFeature) {
      console.log(hotFeature)
      if (!hotFeature) return
      let txtFeature = this.heatmapLayer.getSource().getFeatureById('hotxt')
      if (txtFeature) {
        this.heatmapLayer.getSource().removeFeature(txtFeature)
      }
      // 查看是否已经存在txtPoint对象
      // const sType = feature.getGeometry().getType()
      // if (sType === 'Polygon') {
      //   aPolyCoord = hotFeature.getGeometry().getCoordinates()
      //   // 计算范围框
      //   // 计算中心点
      //   // 创建信息点对象
      //   // 添加到heatmap层中
      // }
      // 计算范围框
      const aGridBBOX = this.getHeatgridBBox(hotFeature)
      // console.log(aGridBBOX)
      // 计算中心点
      const px = (aGridBBOX[0] + aGridBBOX[2]) / 2
      const py = (aGridBBOX[1] + aGridBBOX[3]) / 2
      // 创建text信息点
      // 生成Poitn的Feature
      txtFeature = new Feature({ // ol.Feature
        geometry: new Point([px, py]), // ol.geom.Point
        type: 'hotxt' // 可以省略
      })
      console.log(px, py, txtFeature)
      // poiFeature = this.FeatureLayer.getSource().addFeature(oSource)
      txtFeature.setId('hotxt') // 获取文字feature时使用
      // console.log(this.getPOIStyle (oSource.properties))
      // let txtStyle = new Style({ // ol.style.Style
      //   text: new Text({ // ol.style.Text 文本样式
      //     // font: '12px Calibri,sans-serif',
      //     text: '800',
      //     fill: new Fill({ // ol.style.Fill
      //       color: '#000'
      //     }),
      //     stroke: new Stroke({ // ol.style.Stroke
      //       color: '#fff',
      //       width: 2
      //     })
      //   })
      // })
      // ---
      // txtFeature.setStyle(txtStyle)
      let shotVal = null
      if (hotFeature.get('iVal')) {
        shotVal = hotFeature.get('iVal').toString()
      } else {
        shotVal = ''
      }
      txtFeature.setStyle(new Style({
        text: new Text({
          // font: '10px sans-serif' 默认这个字体，可以修改成其他的，格式和css的字体设置一样
          text: shotVal,
          textAlign: 'center',
          fill: new Fill({
            color: '#333'
          }),
          stroke: new Stroke({ // ol.style.Stroke
            color: '#fff',
            width: 1
          })
        })
      }))
      this.heatmapLayer.getSource().addFeature(txtFeature)
    }

    // 当前方法
  }
}
